<?php

Raise::load('core.RaiseDataType');
Raise::load('core.IRaiseComparable');

/**
 * RaiseTimeSpan class
 * definition of a period of time
 *
 * @author Sam-Mauris Yong <hellclanner at live dot com>
 * @license http://www.opensource.org/licenses/bsd-license New BSD License
 * @package Raise.Core.DateTime
 * @since 1.2
 */
class RaiseTimeSpan extends RaiseDataType implements IRaiseComparable {

    /**
     * Day of the month (1 to 31)
     * @var integer
     */
    private $day = 0;

    /**
     * Hour of the day (0 to 23), 24-hour format
     * @var integer
     */
    private $hour = 0;

    /**
     * Minutes of the hour (0 to 59)
     * @var integer
     */
    private $minute = 0;

    /**
     * Seconds of the minute (0 to 59)
     * @var integer
     */
    private $second = 0;

    /**
     * milliseconds (1/1000th of a second)
     * @var integer
     */
    private $millisecond = 0;

    /**
     * Create a new RaiseTimeSpan based on a period of time in seconds
     * @param integer $a The period of time in seconds
     */
    public function __construct($a = 0){
        if($a < 0){
            throw new RaiseInvalidArgumentException('A time period in negative value is not acceptable in RaiseTimeSpan.');
        }
        $this->day = floor($a / 86400);
        $a -= $this->day * 86400;
        $this->hour = floor($a / 3600);
        $a -= $this->hour * 3600;
        $this->minute = floor($a / 60);
        $a -= $this->minute * 60;
        $this->second = $a;
        
    }

    /**
     * The millisecond component of the RaiseDateTime
     * @param integer $a An integer that represents millisecond (1000th of a second)
     * @return integer
     */
    public function millisecond($a = null){
        if(func_num_args() == 1){
            if($a < 0){
                throw new RaiseInvalidArgumentException('A time period in negative value is not acceptable in RaiseTimeSpan.');
            }
            $this->millisecond = $a;
        }
        return $this->millisecond;
    }

    /**
     * The seconds component of the RaiseDateTime
     * @param integer $a An integer from 0 to 59 that represents the 60 seconds in 1 minute
     * @return integer
     */
    public function second($a = null){
        if(func_num_args() == 1){
            if($a < 0){
                throw new RaiseInvalidArgumentException('A time period in negative value is not acceptable in RaiseTimeSpan.');
            }
            $this->second = $a;
        }
        return $this->second;
    }

    /**
     * The minute component of the RaiseDateTime
     * @param integer $a An integer from 0 to 59 that represents the 60 minutes in 1 hour
     * @return integer
     */
    public function minute($a = null){
        if(func_num_args() == 1){
            if($a < 0){
                throw new RaiseInvalidArgumentException('A time period in negative value is not acceptable in RaiseTimeSpan.');
            }
            $this->minute = $a;
        }
        return $this->minute;
    }

    /**
     * The hour component of the RaiseDateTime
     * @param integer $a An integer from 0 to 23 that represents 24 hours
     * @return integer
     */
    public function hour($a = null){
        if(func_num_args() == 1){
            if($a < 0){
                throw new RaiseInvalidArgumentException('A time period in negative value is not acceptable in RaiseTimeSpan.');
            }
            $this->hour = $a;
        }
        return $this->hour;
    }

    /**
     * The day component of the RaiseDateTime
     * @param integer $a An integer from 1 to 31 representing the days of a month
     * @return integer
     */
    public function day($a = null){
        if(func_num_args() == 1){
            if($a < 0){
                throw new RaiseInvalidArgumentException('A time period in negative value is not acceptable in RaiseTimeSpan.');
            }
            $this->day = $a;
        }
        return $this->day;
    }

    /**
     * Get the total number of seconds in this time span
     * @return integer|double
     */
    public function totalSeconds(){
        return $this->day * 86400 + $this->hour * 3600 + $this->minute * 60 + $this->second + ($this->millisecond / 1000);
    }

    /**
     * Get the total number of minutes in this time span
     * @return integer|double
     */
    public function totalMinutes(){
        return $this->day * 1440 + $this->hour * 60 + $this->minute + ($this->second / 60) + ($this->millisecond / 60000);
    }

    /**
     * Get the total number of hours in this time span
     * @return integer|double
     */
    public function totalHours(){
        return $this->day * 24 + $this->hour + ($this->minute / 60) + ($this->second / 3600) + ($this->millisecond / 3600000);
    }

    /**
     * Get the total number of days in this time span
     * @return integer|double
     */
    public function totalDays(){
        return $this->day + ($this->hour / 24) + ($this->minute / 1440) + ($this->second / 86400) + ($this->millisecond / 86400000);
    }

    /**
     * Compare to another RaiseTimeSpan
     * @param RaiseTimeSpan $o2
     */
    public function compareTo($o2) {
        return $this->totalSeconds() < $o2->totalSeconds() ? -1 : 1;
    }

    /**
     * Add with another RaiseTimeSpan
     * @param RaiseTimeSpan $o2 The amount of time to add
     * @return RaiseTimeSpan The resulting RaiseTimeSpan from the add operation
     */
    public function add($o2){
        $temp = $this->raiseClone();
        $temp->millisecond += $o2->millisecond;
        if($temp->millisecond > 999){
            $temp->millisecond -= 1000;
            $temp->second++;
        }

        $temp->second += $o2->second;
        if($temp->second > 59){
            $temp->second -= 60;
            $temp->minute++;
        }

        $temp->minute += $o2->minute;
        if($temp->minute > 59){
            $temp->minute -= 60;
            $temp->hour++;
        }

        $temp->hour += $o2->hour;
        if($temp->hour > 23){
            $temp->hour -= 24;
            $temp->day++;
        }

        $temp->day += $o2->day;

        return $temp;
    }

    /**
     * Subtract with another RaiseTimeSpan to give a new RaiseTimeSpan
     * @param RaiseTimeSpan $o2 The amount of time to deduct
     * @return RaiseTimeSpan The resulting RaiseTimeSpan from the subtract operation
     */
    public function subtract($o2){
        if($this->totalSeconds() < $o2->totalSeconds()){
            throw new RaiseInvalidArgumentException('A time period in negative value is not acceptable in RaiseTimeSpan.');
        }
        $temp = $this->raiseClone();
        $temp->millisecond -= $o2->millisecond;
        $temp->second -= $o2->second;
        $temp->minute -= $o2->minute;
        $temp->hour -= $o2->hour;
        $temp->day -= $o2->day;
        return $temp;
    }
    
}
